package com.jade.framework.base.text;

import java.io.IOException;
import java.io.Writer;
import java.nio.ByteBuffer;

/**
 * 简单转义工具。
 *
 * @author sky
 * @version 1.0 14-9-22 下午4:26
 */
public class SimpleEscaper implements Escaper
{
    private static SimpleEscaper escaper = new SimpleEscaper();

    protected SimpleEscaper()
    {
    }

    public static Escaper getInstance()
    {
        return escaper;
    }

    public void escape(char[] chars, int off, int len, Writer out)
            throws IOException
    {
        int end = off + len;
        char ch;
        char[] buf = new char[]{'\\', ' '};
        for (int i = off; i < end; i++) {
            ch = chars[i];
            switch (ch) {
                case '\\':
                    buf[1] = '\\';
                    out.write(buf);
                    break;
                case '\t':
                    buf[1] = 't';
                    out.write(buf);
                    break;
                case '\r':
                    buf[1] = 'r';
                    out.write(buf);
                    break;
                case '\n':
                    buf[1] = 'n';
                    out.write(buf);
                    break;
                case '\f':
                    buf[1] = 'f';
                    out.write(buf);
                    break;
                case '|':
                    buf[1] = '|';
                    out.write(buf);
                    break;
                default:
                    out.write(ch);
            }
        }
    }

    public void escape(String str, int off, int len, Writer out)
            throws IOException
    {
        int end = off + len;
        char ch;
        char[] buf = new char[]{'\\', ' '};
        for (int i = off; i < end; i++) {
            ch = str.charAt(i);
            switch (ch) {
                case '\\':
                    buf[1] = '\\';
                    out.write(buf);
                    break;
                case '\t':
                    buf[1] = 't';
                    out.write(buf);
                    break;
                case '\r':
                    buf[1] = 'r';
                    out.write(buf);
                    break;
                case '\n':
                    buf[1] = 'n';
                    out.write(buf);
                    break;
                case '\f':
                    buf[1] = 'f';
                    out.write(buf);
                    break;
                case '|':
                    buf[1] = '|';
                    out.write(buf);
                    break;
                default:
                    out.write(ch);
            }
        }
    }

    public static StringBuffer escape(StringBuffer sb, String str)
    {
        return escape(sb, str, true);
    }

    public static StringBuffer escape(StringBuffer sb, String str, boolean v)
    {
        return escape(sb, str, 0, str.length(), v);
    }

    public static StringBuffer escape(StringBuffer sb, String str, int off, int len)
    {
        return escape(sb, str, off, len, true);
    }

    public static StringBuffer escape(StringBuffer sb, String str, int off, int len, boolean v)
    {
        int end = off + len;
        char ch;
        for (int i = off; i < end; i++) {
            ch = str.charAt(i);
            switch (ch) {
                case '\\':
                    sb.append("\\\\");
                    break;
                case '\t':
                    sb.append("\\t");
                    break;
                case '\r':
                    sb.append("\\r");
                    break;
                case '\n':
                    sb.append("\\n");
                    break;
                case '\f':
                    sb.append("\\f");
                    break;
                case '|':
                    if (v) {
                        sb.append("\\|");
                    }
                    else {
                        sb.append('|');
                    }
                    break;
                default:
                    sb.append(ch);
            }
        }
        return sb;
    }

    private static final byte E_S = (byte)'\\';
    private static final byte E_T = (byte)'\t';
    private static final byte E_N = (byte)'\n';
    private static final byte E_R = (byte)'\r';
    private static final byte E_F = (byte)'\f';
    private static final byte E_V = (byte)'|';

    private static final byte T = (byte)'t';
    private static final byte N = (byte)'n';
    private static final byte R = (byte)'r';
    private static final byte F = (byte)'f';

    public String unescape(char[] chars, int off, int len)
    {
        return unescape0(chars, off, len);
    }

    private static String unescape0(char[] chars)
    {
        return unescape0(chars, 0, chars.length);
    }

    private static String unescape0(char[] chars, int off, int len)
    {
        StringBuffer sb = new StringBuffer(len);
        int end = off + len;
        char ch1, ch2;
        for (int i = off; i < end; i++) {
            ch1 = chars[i];
            if (ch1 == '\\' && i + 1 < end) {
                ch2 = chars[++i];
                switch (ch2) {
                    case '\\':
                        sb.append('\\');
                        break;
                    case 't':
                        sb.append('\t');
                        break;
                    case 'r':
                        sb.append('\r');
                        break;
                    case 'n':
                        sb.append('\n');
                        break;
                    case 'f':
                        sb.append('\f');
                        break;
                    case '|':
                        sb.append('|');
                        break;
                    default:
                        //Maybe wrong, append them.
                        sb.append('\\').append(ch2);
                }
            }
            else {
                sb.append(ch1);
            }
        }
        return sb.toString();
    }

    public static String unescape(String str)
    {
        char[] chars = str.toCharArray();
        return unescape0(chars);
    }

    public static String unescape(String str, int off, int len)
    {
        char[] chars = new char[len];
        str.getChars(off, off + len, chars, 0);
        return unescape0(chars);
    }

    public String unescape(byte[] bytes, int off, int len, String encoding)
    {
        ByteBuffer buffer = null;
        byte[] buf = null;
        if (len > 8192) {
            buf = new byte[len];
        }
        else {
            buffer = ByteBuffer.allocate(len);
            buf = buffer.array();
        }

        int end = off + len;
        byte b1, b2;
        int index = 0;
        for (int i = off; i < end; i++) {
            b1 = bytes[i];
            if (b1 == E_S && i + 1 < end) {
                b2 = bytes[++i];
                switch (b2) {
                    case E_S:
                        buf[index++] = E_S;
                        break;
                    case T:
                        buf[index++] = E_T;
                        break;
                    case R:
                        buf[index++] = E_R;
                        break;
                    case N:
                        buf[index++] = E_N;
                        break;
                    case F:
                        buf[index++] = E_F;
                        break;
                    case E_V:
                        buf[index++] = E_V;
                        break;
                    default:
                        //Maybe wrong, append them.
                        buf[index++] = E_S;
                        buf[index++] = b2;
                }
            }
            else {
                buf[index++] = b1;
            }
        }
        try {
            return new String(buf, 0, index, encoding);
        }
        catch (Exception e) {
            return new String(buf, 0, index);
        }
    }


    public static void main(String[] args)
    {
        StringBuffer sb = new StringBuffer();
        escape(sb, args[0]);
        String str = sb.toString();
        System.out.println(str);
        str = unescape(str);
        System.out.println(str);
    }

}
